#include "myFunctions.h"
#include "bpNet.h"

using namespace std;

int main(int argc,char *argv[]){
	bpNet Network;
	time_t start,end;
	int nEntradas, nSalidas, capas, K,p;
	loadConfig(capas);
	int neurons[capas];
	loadConfig(nEntradas,nSalidas,neurons,K,capas);
	/*int nEntradas = 4, //ver iris.csv
	    nSalidas = 3, //clase 0,1,2 respectivamente
	    neurons[] = {nEntradas,30,10,nSalidas}, //Cargo topologia de la red
			K; //para el Leave-K-ut*/
	mdouble datos1a,
					datos1b,
					datos1c,
					X;
	float **vRand,**targets,
	      **vRand_test,**targets_test,
	      *res = (float*)calloc(nSalidas,sizeof(float)),
				tiempo;
	vector<int> y,
				indRandomize; //vector para acceso "NO secuencial" (aleatorio) de patrones
	vector<string>paths;
  
	//Cargo el iris.csv sin fixa
	readFile(X,y,"iris.csv",false); //X(X.size(),4) y(X.size,1)
	int numPatterns = X.size();
	
	//Leo el K
	bool flag = false;
	while (!flag){
		//cout<< "Ingrese K para leave-k-out: ";
		cout<< "K para leave-k-out: "<< K << endl;
		//cin>>K;
		if(K<numPatterns) flag = !flag;
	}

	//Aleatorizo la seleccion mediante un suffle de indices en el vector de indices
  indRandomize.resize(numPatterns);
	for(int k=0;k<numPatterns;k++) indRandomize[k] = k;
	random_shuffle(indRandomize.begin(),indRandomize.end());

	int i,j, //Indices
			maxEpocas = 100000,
			numPatTrain=numPatterns-K;
	double deltaError = 0.04,
					error,
					Eav = 0,
					desvStand = 0;
  
	//Aloco memoria 
	//Para los primeros NumeroDePatrones-K que es el total de training	
	vRand = (float**)calloc(numPatTrain,sizeof(float*));
	targets = (float **)calloc(numPatTrain,sizeof(float*));
	//Para los segundos K que es el total de validacion
	vRand_test = (float**)calloc(K,sizeof(float*));
	targets_test = (float**)calloc(K,sizeof(float*));
	for(i=0; i<numPatTrain; i++){
		vRand[i] = (float*)calloc(nEntradas,sizeof(float));
		targets[i] = (float*)calloc(nSalidas,sizeof(float));
	}     
	for(i=0; i<K; i++){
		vRand_test[i] = (float*)calloc(nEntradas,sizeof(float));
		targets_test[i] = (float*)calloc(nSalidas,sizeof(float));
	}   
	int iters = floor(numPatterns/K);
	vector<float> vError,vTime;
	vError.resize(iters);
	vTime.resize(iters);
	for(p=0;p<iters;p++){
		error=0;
		//Cargamos datos training ramdomizados, tanto entradas como salidas
		int itest=0,itraining=0;		
		for (i=(p-1)*K;i<p*K;i++){
			if(i<0) break;
			for (j=0;j<nEntradas;j++) vRand[itraining][j] = X[indRandomize[i]][j]; //Asignamos a vRand vectores filas de X randomizadas.
			if (!y[indRandomize[i]]){ // =º=0 Observar como tenemos 3 perc de salida van 3 targets y tambien las asignaciones...		
				targets[itraining][0] = 1; 		 
				targets[itraining][1] = -1;
				targets[itraining][2] = -1;
			}
			else{
				if (y[indRandomize[i]]==1){ // ==1	
					targets[itraining][0] = -1; 		 
					targets[itraining][1] = 1;
					targets[itraining][2] = -1;
				}
				else{ // ==2
					targets[itraining][0] = -1; 		 
					targets[itraining][1] = -1;
					targets[itraining][2] = 1;
				}
				itraining++;
			}
		}
		for (i=(p+1)*K;i<numPatterns;i++){
			for (j=0;j<nEntradas;j++) vRand[itraining][j] = X[indRandomize[i]][j]; //Asignamos a vRand vectores filas de X randomizadas.
			if (!y[indRandomize[i]]){ // ==0 Observar como tenemos 3 perc de salida van 3 targets y tambien las asignaciones...		
				targets[itraining][0] = 1; 		 
				targets[itraining][1] = -1;
				targets[itraining][2] = -1;
			}
			else{
				if (y[indRandomize[i]]==1){ // ==1	
					targets[itraining][0] = -1; 		 
					targets[itraining][1] = 1;
					targets[itraining][2] = -1;
				}
				else{ // ==2
					targets[itraining][0] = -1; 		 
					targets[itraining][1] = -1;
					targets[itraining][2] = 1;
				}
			}
			itraining++;
		}
		//Cargamos datos test ramdomizados, tanto entradas como salidas
		for (i=p*K;i<K*(p+1);i++){
			for (j=0;j<nEntradas;j++) vRand_test[itest][j] = X[indRandomize[i]][j]; //Los de test estan a lo ultimo
			if (!y[indRandomize[i]]){		
				targets_test[itest][0] = 1; 		 
				targets_test[itest][1] = -1;
				targets_test[itest][2] = -1;
			}
			else{
				if (y[indRandomize[i]]==1){		
					targets_test[itest][0] = -1; 		 
					targets_test[itest][1] = 1;
					targets_test[itest][2] = -1;
				}
				else{
					targets_test[itest][0] = -1; 		 
					targets_test[itest][1] = -1;
					targets_test[itest][2] = 1;
				}
			}
			itest++;
		}
		Network.init(neurons,capas); //Inicializo la red, training, test y default
		Network.RandomWeights(); //Pesos ramdom para iniciar el algoritmo de entrenamiento
		time(&start);
		if (Network.BpTrain(vRand,targets,numPatterns-K,maxEpocas,deltaError)) { 
			time(&end);
			cout<<"Entreno"<<endl;
			for (i = 0; i < K; i++){
				bool acierta = 1;
				res = Network.getOutput(vRand_test[i]); 
				for (j=0;j<nSalidas;j++){
					//cout<<res[j]<<endl;
					res[j] = sign(res[j]);
					if(res[j]!=targets_test[i][j]){ 
						acierta=0;			
						break;
					}//if
				}//for j
				if(!acierta) error++;	
			}//for i
			vError[p]=error/(double)K;
			vTime[p]=difftime(end,start);
		}//if
		else cout<<"Falta entrenar"<< endl; 
	}//p

	Eav = promedio(vError);
	desvStand = desvio(vError);
	tiempo = promedio(vTime);
	saveInfo(neurons,K,Eav,desvStand,capas,tiempo,deltaError);
	return 1;
}
